import * as mapvgl from 'mapvgl'
import { FanLayer, FlyLineLayer } from 'mapvgl/dist/mapvgl.threelayers.min'
import { CanvasCtx } from './selectedBox'
import { qtTransPort } from './linkQt'
import { translate } from './utils'
import { getCurveByTwoPoints, generateId } from './utils'
import { NetLink } from './netLine'
import redImg from '@/image/red_plane.png'
import blueImg from '@/image/blue_car.png'
import blueBase from '@/image/blue_base.png'
import markerImg from '@/image/marker.png'

const DEFAULT_SETTINGS = {
  RADAR: false,
  FLY_LINE: true,
  CLICK_EVTS: ['click', 'dragend', 'dragstart', 'dblclick', 'mousedown'],
  FTP_NUM: 0.5, // 会出现掉帧情况，所以会进行一个参数适配
}

const scanLayer = {
  scout: null,
  link: null,
}

function initScanLayer(view) {
  // 侦察
  scanLayer.scout = new ScanLayer(view, {
    color: '#ff0000',
    type: 'radar',
    data: [],
    size: 100,
    radius: 100,
    antialias: false,
    duration: 5,
    trail: -200,
  })
  scanLayer.link = new ScanLayer(view, {
    // 绘制带波纹扩散的圆
    type: 'wave',
    // 扩散半径，支持直接设置和回调两种形式
    radius: 100,
    // 周期影响扩散速度，越小越快
    duration: 1 / 2,
    // 拖尾影响波纹数，越大越多
    trail: 20,
    data: [],
  })
}
export class Camp {
  /**
   * @param {Array} coors
   * @param {Array} lines
   * @param {Number} type 0:red or 1:blue
   * @param {Object} view
   * @param {Object} map
   */
  constructor(coors, lines, view, map) {
    this.view = view
    this.map = map
    this.redCoors = coors[0]
    this.blueCoors = coors[1]
    this.lines = lines
    this.animateHz = 30
    // 通用曲线
    this.curve = new mapvgl.BezierCurve()
    this.lineCurve = new mapvgl.GeodesicCurve()
    this.odCurve = new mapvgl.OdCurve()
    this.commVisible = DEFAULT_SETTINGS.RADAR
    this.isFlyline = DEFAULT_SETTINGS.FLY_LINE
    this.layerLines = [] // 通信 干扰
    this.statusList = [0, 1, 2, 3] // 红不同 0 蓝不同  1 红同  2 蓝同 3 代表不同线的种类
    this.prevAddLinkItem = null
    this.init()
  }
  init() {
    initScanLayer(this.view)
    this.items = [...this.getItems(this.redCoors, 0), ...this.getItems(this.blueCoors, 1)]
    this.initDynamicLines()
    this.generateLineLayers()
  }
  initDynamicLines() {
    this.lines.forEach(({ start, end }) => {
      this.curve.setOptions({ start, end })
      const current = {
        geometry: {
          type: 'LineString',
          coordinates: this.curve.getPoints(),
        },
        properties: {
          count: Math.random(),
          status: this.getCampStatus({ start, end }),
          start,
          end,
        },
      }
      this.layerLines.push(current)
    })
  }
  generateLineLayers() {
    const flylineProperties = {
      style: 'normal',
      step: 0.32,
      color: 'rgba(33, 242, 214, 0.8)',
      textureColor: '#ff0000',
      textureWidth: 30,
      textureLength: 60,
    }
    const blueFlyLineProperties = {
      style: 'normal',
      step: 0.32,
      color: 'rgba(33, 242, 214, 0.8)',
      textureColor: '#56ccdd',
      textureWidth: 30,
      textureLength: 60,
    }
    const lineFlowProperties = {
      interval: 0.2,
      duration: 1,
      trailLength: 0.5,
      zoom: 4,
      color: '#ff0000',
      width: 4,
      animation: true,
    }
    const blueLineFlowProperties = {
      interval: 0.2,
      duration: 1,
      trailLength: 0.5,
      zoom: 4,
      color: '#56ccdd',
      width: 4,
      animation: true,
    }
    this.lineLayers = this.statusList.map((status) => {
      let layer = null
      switch (status) {
        case 0:
          layer = new FlyLineLayer(flylineProperties)
          break
        case 1:
          layer = new FlyLineLayer(blueFlyLineProperties)
          break
        case 2:
          layer = new mapvgl.LineLayer(lineFlowProperties)
          break
        case 3:
          layer = new mapvgl.LineLayer(blueLineFlowProperties)
          break
      }
      this.view.addLayer(layer)
      return layer
    })
    this.updateLinkLine()
  }
  generateLinkdata(list, item) {
    const linksData = []
    for (let i = 0; i < list.length; i++) {
      const { start, end, status } = list[i].properties
      const coorStr = item.coor.toString()
      if (start.toString() === coorStr || end.toString() === coorStr) {
        if (start.toString() === coorStr) {
          linksData.push({ end, status })
        } else {
          linksData.push({ start, status })
        }
        list.splice(i, 1)
        i--
      }
    }
    return linksData
  }
  addMarker(coor, type, name) {
    let index = -1
    if (type === 0) {
      index = this.redCoors.length
      this.redCoors.push(coor)
    } else {
      index = this.blueCoors.length
      this.blueCoors.push(coor)
    }
    const label = name || this.getLabel(index, type)
    this.items.push(new Unit(coor, type + '-' + index, label, this))
  }
  addLinks(list, coor) {
    list.forEach((item) => {
      const { start = coor, end = coor } = item
      this.curve.setOptions({ start, end })
      this.addNewLayerLine(
        { start, end },
        {
          geometry: {
            type: 'LineString',
            coordinates: this.curve.getPoints(),
          },
          properties: {
            count: Math.random(),
            status: item.status,
            start,
            end,
          },
        }
      )
    })
  }
  delLink(line) {
    const index = this.layerLines.findIndex((item) => {
      const { start, end, status } = item.properties
      if (start.toString() === line.start.toString() && end.toString() === line.end.toString()) {
        return true
      }
    })
    if (index > -1) {
      this.layerLines.splice(index, 1)
    }
  }
  addNewLayerLine({ start, end }, line) {
    const target = this.layerLines.find((item) => {
      const itemStart = item.properties.start
      const itemEnd = item.properties.end
      const c1 = start.toString() === itemStart.toString()
      const c2 = start.toString() === itemEnd.toString()
      const c3 = end.toString() === itemStart.toString()
      const c4 = end.toString() === itemEnd.toString()
      const c5 = this.isInSameCamp(start, end)
      if ((c1 && c4) || (c2 && c3 && c5)) {
        return true
      }
      return false
    })
    if (!target) {
      this.layerLines.push(line)
    }
  }
  setPrevAddLink(item) {
    this.prevAddLinkItem = item
  }
  delItem(target) {
    const index = this.items.findIndex((item) => item === target)
    this.items.splice(index, 1)
    this.handleLinkLayer(target)
    this.hideLines = []
  }
  updateLinkLine() {
    if (this.isFlyline) {
      const diffLinesByKinds = this.getDiffLinesByKinds()
      this.lineLayers.forEach((layer, index) => {
        layer.setData(diffLinesByKinds[index])
      })
    }
  }
  updateCoors(type, oldVal, newVal) {
    const coors = type ? this.blueCoors : this.redCoors
    const index = coors.findIndex((item) => oldVal.toString() === item.toString())
    if (index > -1) {
      coors.splice(index, 1, newVal)
    }
  }
  handleLinkLayer(item) {
    this.hideLines = this.generateLinkdata(this.layerLines, item)
    this.updateLinkLine()
  }
  getItems(coors, type) {
    return coors.map((coor, index) => {
      const label = this.getLabel(index, type)
      return new Unit(coor, type + '-' + index, label, this)
    })
  }
  getLabel(index, type) {
    if (type === 0) {
      return index === 0 ? '红父节点' : '红子节点' + index
    }
    return index === 0 ? '蓝父节点' : '蓝子节点' + index
  }
  getCampStatus({ start, end }) {
    let status = -1 //  初始状态  -1  红不同 0 蓝不同  1 红同  2 蓝同 3
    if (this.isInRed(start)) {
      status = 0
    }
    if (this.isInBlue(start)) {
      status = 1
    }
    this.redCoors.find((coor) => {
      if (end.toString() === coor.toString() && status === 0) {
        status = 2
      }
    })
    this.blueCoors.find((coor) => {
      if (end.toString() === coor.toString() && status === 1) {
        status = 3
      }
    })
    return status
  }
  getDiffLinesByKinds() {
    const list = [[], [], [], []]
    this.layerLines.forEach((line) => {
      list[line.properties.status].push(line)
    })
    return list
  }
  isInRed(coor) {
    return !!this.redCoors.find((item) => item.toString() === coor.toString())
  }
  isInBlue(coor) {
    return !!this.blueCoors.find((item) => item.toString() === coor.toString())
  }
  isInSameCamp(start, end) {
    const isRed = this.isInRed(start)
    const isBlue = this.isInBlue(end)
    if (isRed && isBlue) {
      return false
    } else if (!isBlue && !isRed) {
      return false
    } else {
      return true
    }
  }
  find(key, value) {
    return this.items.find((item) => item[key].toString() === value)
  }
  resetLinkLineLayer(coor) {
    this.addLinks(this.hideLines, coor)
    this.hideLines = []
    this.updateLinkLine()
  }
  hideSelectedBoxs() {
    this.items.forEach((item) => {
      item.hideSelectedBox()
    })
  }
  openFlyline() {
    if (this.isFlyline) {
      return
    }
    this.isFlyline = true
    this.updateLinkLine()
  }
  closeFlyline() {
    if (!this.isFlyline) {
      return
    }
    this.isFlyline = false
    this.lineLayers.forEach((layer, index) => {
      layer.setData([])
    })
  }
  insertNet(sUnit, tUnit) {
    const net = sUnit.netLinks.find(item => item.id === generateId(sUnit.label, tUnit.label))
    if (net) {
      return
    }
    const netLink = new NetLink(this.map, sUnit, tUnit)
    sUnit.netLinks.push(netLink)
    tUnit.netLinks.push(netLink)
  }
  breakNet(unit, targetName, time) {
    const current = unit.netLinks.find((item) => generateId(unit.label, targetName) === item.id)
    current?.removeSolid(time)
  }
}

export class Unit {
  // type 0-0: 红基站 0-1：红无人机 1-0：蓝基站 1-1：蓝子节点
  constructor(coor, type, label, camp) {
    this.coor = coor
    this.label = label
    this.view = camp.view
    this.map = camp.map
    this.camp = camp
    this.netLinks = []
    this.init(type)
  }
  init(type) {
    this.clickEvts = DEFAULT_SETTINGS.CLICK_EVTS
    this.commVisible = DEFAULT_SETTINGS.RADAR // 是否侦察
    this.scoutVisible = DEFAULT_SETTINGS.RADAR 
    this.seleted = false
    this.fanLayer = scanLayer.link
    if (type.startsWith('0')) {
      this.type = 0
      this.img = redImg
      this.color = '#ff0000'
      this.battleLayer = scanLayer.scout
    } else {
      this.type = 1
      this.img = type.endsWith('0') ? blueBase : blueImg
      this.color = '#56ccdd'
    }
    if (type.endsWith('0')) {
      this.imgSize = 100
      this.fanSize = 150
    } else {
      this.imgSize = 80
      this.fanSize = 100
    }
    this.generateMarker()
    this.generateFanData()
  }
  initEvents() {
    this.clickEvts.forEach((event) => {
      this.marker.addEventListener(event, (e) => {
        switch (event) {
          case 'click':
            const res = e.target.getLabel().content
            qtTransPort(res)
            this.clickEvents(event)
            break
          case 'dragend':
            this.dragend(e)
            break
          case 'mousedown':
            if (e.domEvent.button === 2) {
              const res = e.target.getLabel().content
              qtTransPort(res, 'jsrightcallme')
            }
            break
          case 'dragstart':
            this.dragstart(e)
            break
          // case 'dblclick':
          //   this.dblclick(e)
          //   break
        }
      })
    })
  }
  generateMarker() {
    this.point = new BMapGL.Point(...this.coor)
    const icon = new BMapGL.Icon(this.img, new BMapGL.Size(this.imgSize, this.imgSize))
    this.marker = new BMapGL.Marker(this.point, {
      size: this.imgSize,
      icon,
    })
    this.marker.setLabel(this.setLabelStyle(this.label))
    this.marker.enableDragging()
    this.map.addOverlay(this.marker)
    this.initEvents()
  }
  generateFanData() {
    this.fanData = {
      geometry: {
        type: 'Point',
        coordinates: this.coor,
      },
      color: this.type === 0 ? '#ff0000' : '#000fff',
      size: this.fanSize / 10,
      properties: {
        count: this.fanSize,
      },
    }
  }
  addLink() {
    this.map.closeInfoWindow(this.infoWindowObject)
    this.camp.hideSelectedBoxs()
    if (!this.customOverlay) {
      this.customOverlay = new BMapGL.CustomOverlay(this.createCanvas, {
        point: this.point,
        properties: {
          size: this.imgSize + 40,
          self: this,
        },
        offsetX: 0,
        offsetY: 40,
        enableDraggingMap: false,
      })
      this.map.addOverlay(this.customOverlay)
    } else {
      this.customOverlay.show()
    }
  }
  delNode() {
    this.map.closeInfoWindow()
    this.map.removeOverlay(this.marker)
    this.camp.delItem(this)
    this.fanLayer.hide(this.fanData)
  }
  // 更新经纬度
  updateCoor(coor) {
    this.updateNetLinks(this.coor, coor)
    this.camp.updateCoors(this.type, this.coor, coor)
    this.coor = coor
    this.point = new BMapGL.Point(...this.coor)
  }
  updateNetLinks(oldCoor, newCoor) {
    this.netLinks.forEach((link) => {
      const type = link.start.toString() === oldCoor.toString() ? 0 : 1
      link.updateFeature(newCoor, type)
    })
    this.showNetLines()
  }
  updateFanlayer() {
    this.fanData.geometry.coordinates = this.coor
    if (this.commVisible) {
      this.fanLayer.update(this.fanData)
    }
    if (this.scoutVisible && this.battleLayer) {
      this.battleLayer.update(this.fanData)
    }
  }
  clickEvents() {
    const { prevAddLinkItem } = this.camp
    if (prevAddLinkItem && prevAddLinkItem !== this) {
      prevAddLinkItem.seletedBox.current?.redraw()
      const link = { start: prevAddLinkItem.coor, end: this.coor }
      const list = [
        {
          ...link,
          status: this.camp.getCampStatus(link),
        },
      ]
      this.camp.addLinks(list)
      this.camp.updateLinkLine()
      prevAddLinkItem.hideSelectedBox()
      this.camp.prevAddLinkItem = null
    }
  }
  dragstart() {
    this.closeFanlayer()
    this.closeFanlayer(1)
    this.camp.handleLinkLayer(this)
  }
  dragend(event) {
    const { latLng } = event
    this.updateCoor([latLng.lng, latLng.lat])
    this.updateFanlayer()
    this.camp.resetLinkLineLayer(this.coor)
  }
  move(coor, type, time) {
    // item.hideNetLines(this.isInRed(item.coor) ? this.redLinkNet : this.blueLinkNet)
    this.closeFanlayer()
    this.closeFanlayer(1)
    this.animateHz = Math.floor((time / 1000) * 60 * DEFAULT_SETTINGS.FTP_NUM)
    this.pathPoints = this.generatePathPoints(this.coor, coor, type)
    this.createMovePath(coor)
    this.camp.handleLinkLayer(this)
    this.animateMarker(1)
  }
  // 生成移动路径的点
  generatePathPoints(start, end, type) {
    switch (type) {
      case 0:
        return this.getLinePoints(start, end)
      case 1:
        return this.generateSinPoint(start, end)
      case 2:
        return this.generateSinPoint(start, end, 8)
    }
  }
  generateSinPoint(start, end, parts) {
    const startLngLat = { lng: start[0], lat: start[1] }
    const endLngLat = { lng: end[0], lat: end[1] }
    const points = getCurveByTwoPoints(startLngLat, endLngLat, this.animateHz, parts).map(
      ([lng, lat]) => ({ lng, lat })
    )
    points.push(endLngLat)
    return points
  }
  getLinePoints(start, end) {
    const points = [{ lng: start[0], lat: start[1] }]
    const xd = (end[0] - start[0]) / this.animateHz
    const yd = (end[1] - start[1]) / this.animateHz
    for (let i = 0; i < this.animateHz; i++) {
      const prev = points[points.length - 1]
      points.push({ lng: prev.lng + xd, lat: prev.lat + yd })
    }
    return points
  }
  createMovePath(coor) {
    this.movePathLayer = new mapvgl.SimpleLineLayer({
      width: 8,
      color: this.color,
      blend: 'lighter',
      data: [
        {
          geometry: {
            type: 'LineString',
            coordinates: this.pathPoints.map(({ lng, lat }) => [lng, lat]),
          },
        },
      ],
    })
    this.view.addLayer(this.movePathLayer)
    const myIcon = new BMapGL.Icon(markerImg, new BMapGL.Size(100 / 6, 153 / 6), {
      anchor: new BMapGL.Size(100 / 12, 153 / 6),
    })
    this.pointMarker = new BMapGL.Marker(new BMapGL.Point(...coor), {
      icon: this.type === 0 ? '' : myIcon,
    })
    this.map.addOverlay(this.pointMarker)
  }
  removeMovePath() {
    this.view.removeLayer(this.movePathLayer)
    this.movePathLayer = null
    this.map.removeOverlay(this.pointMarker)
    this.pointMarker = null
  }
  animateMarker(num) {
    // 计算下一帧的位置
    // 这里可以使用缓动函数来获得更平滑的动画效果
    // 更新 marker 的位置
    // marker.coordinates = [newLongitude, newLatitude];

    if (num > this.animateHz) {
      const { lng, lat } = this.pathPoints.pop()
      this.updateCoor([lng, lat])
      this.updateFanlayer()
      this.camp.resetLinkLineLayer([lng, lat])
      this.removeMovePath()
      return
    }
    // 继续下一帧动画
    requestAnimationFrame(() => this.animateMarker(++num))
    const current = this.pathPoints[num]
    // 重绘地图
    this.setPosition(current)
  }
  dblclick(event) {
    this.infoWindowObject = new BMapGL.InfoWindow(this.createWindowHtml(), {
      title: '详情',
      width: 50,
    })
    this.map.openInfoWindow(this.infoWindowObject, this.point)
    setTimeout(() => {
      this.registerInfoEvents()
    }, 500)
  }
  createWindowHtml() {
    return `
      <div id="main" style="padding-bottom: 12px">
        <div class="info">
          <span>名称：</span>
          <span>${this.label}</span>
        </div>   
        <div class="info">
          <span>坐标：</span>
          <span>${this.coor[0].toFixed(6)},${this.coor[1].toFixed(6)}</span>
        </div>
        <button type="button" class="btn btn-outline-primary btn-sm" id="delNode">删除该节点</button>
        <button type="button" class="btn btn-outline-primary btn-sm" id="addLink">新增通抗</button>
      </div>
    `
  }
  registerInfoEvents() {
    const dom = document.getElementById('main')
    if (!dom) {
      return
    }
    dom.addEventListener('click', (e) => {
      switch (e.target.id) {
        case 'delNode':
          this.delNode()
          break
        case 'addLink':
          this.addLink()
          break
      }
    })
  }
  createCanvas(config) {
    const canvas = document.createElement('canvas')
    config.self.seletedBox = new CanvasCtx(canvas, config.size, config.self)
    return canvas
  }
  hideSelectedBox() {
    this.customOverlay && this.customOverlay.hide()
  }
  setLabelStyle(content) {
    //左偏移  右偏移
    var offsetSize = new BMapGL.Size(0, 0)
    var labelStyle = {
      color: '#fff',
      backgroundColor: '#333333',
      border: '0',
      fontSize: '14px',
      width: '80px',
      opacity: '0.8',
      verticalAlign: 'center',
      borderRadius: '2px',
      whiteSpace: 'normal',
      wordWrap: 'break-word',
      padding: '7px',
    }
    //用于设置样式
    var spanA = "<span class='angle'><span>"
    //不同数字长度需要设置不同的样式。
    var num = parseInt(content.length / 10)
    switch (num) {
      case 0:
        offsetSize = new BMapGL.Size(-20, -40)
        break
      case 1:
        offsetSize = new BMapGL.Size(-20, -40)
        break
      case 2:
        offsetSize = new BMapGL.Size(-20, -60)
        break
      case 3:
        offsetSize = new BMapGL.Size(-20, -80)
        break
      default:
        break
    }

    var label = new BMapGL.Label(content, {
      offset: offsetSize,
    })
    label.setStyle(labelStyle)
    return label
  }
  openFnalayer(type = 0) {
    if (type === 0) {
      this.fanLayer.show(this.fanData)
      this.coummVisible = true
    } else {
      this.battleLayer.show(this.fanData)
      this.scoutVisible = true
    }
  }
  closeFanlayer(type = 0) {
    if (type === 0) {
      this.fanLayer.hide(this.fanData)
      this.coummVisible = false
    } else {
      if (!this.battleLayer) {
        return
      }
      this.battleLayer.hide(this.fanData)
      this.scoutVisible = false
    }
  }
  hideNetLines() {
    for (let i = 0; i < this.netLinks.length; i++) {
      const cur = this.netLinks[i]
      cur.hide()
    }
  }
  showNetLines() {
    for (let i = 0; i < this.netLinks.length; i++) {
      const cur = this.netLinks[i]
      cur.show()
    }
  }
  setPosition(current) {
    this.marker.setPosition(current)
  }
}

class ScanLayer {
  constructor(view, options = {}) {
    this.instance = new mapvgl.CircleLayer(options)
    this.data = []
    view.addLayer(this.instance)
  }
  show(item) {
    const index = this.data.findIndex(it => it === item)
    if (index > -1) {
      return
    }
    this.data.push(item)
    this.instance.setData(this.data)
  }
  hide(item) {
    const index = this.data.findIndex(it => it === item)
    if (index < 0) {
      return
    }
    this.data.splice(index, 1)
    this.instance.setData(this.data)
  }
  update() {
    this.instance.setData(this.data)
  }
}
